package utils

import (
	"abdis/interface/database"
	"fmt"
	"sort"
	"time"
)

func ToCmdLine(cmd ...string) [][]byte {
	return StringsToBytes(cmd)
}

func ToCmdLineRaw(cmd string, args ...[]byte) [][]byte {
	result := make([][]byte, len(args)+1)
	result[0] = []byte(cmd)
	for i, a := range args {
		result[i+1] = a
	}
	return result
}

func BytesEquals(a []byte, b []byte) bool {
	if (a == nil && b != nil) || (a != nil && b == nil) {
		return false
	}
	size := len(a)
	if size != len(b) {
		return false
	}

	for i := 0; i < size; i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func Equals(a any, b any) bool {
	ba, oka := a.([]byte)
	bb, okb := b.([]byte)
	if oka && okb {
		return BytesEquals(ba, bb)
	}
	return a == b
}

// Fnv32 哈希算法
func Fnv32(key string) uint32 {
	const prime32 = uint32(16777619)
	hash := uint32(2166136261)
	for i := 0; i < len(key); i++ {
		hash *= prime32
		hash ^= uint32(key[i])
	}
	return hash
}

func ToLockIndices(keys []string, reverse bool, spread func(uint32) uint32) []uint32 {
	indexMap := make(map[uint32]struct{})
	for _, key := range keys {
		index := spread(Fnv32(key))
		indexMap[index] = struct{}{}
	}
	indices := make([]uint32, 0, len(indexMap))
	for index := range indexMap {
		indices = append(indices, index)
	}
	sort.Slice(indices, func(i, j int) bool {
		if !reverse {
			return indices[i] < indices[j]
		}
		return indices[i] > indices[j]
	})
	return indices
}

func StringsToBytes(strSlice []string) [][]byte {
	byteSlice := make([][]byte, len(strSlice))
	for i, str := range strSlice {
		byteSlice[i] = []byte(str)
	}
	return byteSlice
}

func BytesToStrings(bytes [][]byte) []string {
	strs := make([]string, len(bytes))
	for i, b := range bytes {
		strs[i] = string(b)
	}
	return strs
}

func AdjustIndex(size int, index int) int {
	if index < 0 {
		index *= -1
	}
	return index % size
}

func GetListRange(size int, begin int, end int) (start int, stop int) {
	if begin < -size {
		begin = 0
	} else if begin < 0 {
		begin += size
	} else if begin >= size {
		begin %= size
	}

	if end < -size {
		end = 0
	} else if end < 0 {
		end += size + 1
	} else if end < size {
		end += 1
	} else {
		end = size
	}

	if begin < end {
		return begin, end
	}

	return end, begin
}

func CheckAuth(requirepass string, passwd string) bool {
	if requirepass == "" {
		return true
	}
	return requirepass == passwd
}

func ToExpireCmd(key string, t *time.Time) database.CmdLine {
	expireAt := t.UnixNano() / 1e6
	return ToCmdLineRaw("PExpireAt", []byte(key), []byte(fmt.Sprintf("%d", expireAt)))
}
